﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace InvasionWars
{
    public class Server
    {

        public string ServerName{get; set;}
        public IPAddress ServerIP { get; set; }
        public int Port{get; set;}
        public ServerConnectionState connState;
        //probably need protocol
        //              communication object
        private TcpClient client;
        //private Thread readThread;
        private NetworkStream socketStream;
        //private Socket connection;
        private BinaryReader reader;
        private BinaryWriter writer;
        public GameState State { get; set; }
        public Server()
        {

            State = GameState.WAITING;
        }

        public int Connect()
        {
            //attempt Connection.
            //if server connect is successful return 0
            //if server connect failed return non-zero or exception
            client = new TcpClient(ServerName, Port);
            
            //client.Connect(ServerIP, Port);

            socketStream = client.GetStream();

            reader = new BinaryReader(socketStream);
            
            int newPort = reader.ReadInt16();
            

            if (newPort > Port)
            {
                Port = newPort;
                client.Close();
                client = new TcpClient(ServerName, Port);
                socketStream = client.GetStream();
                State = GameState.COMMUNICATING;
                connState = ServerConnectionState.CONNECTED;

                
                return 0;
            }
            else return -1;



            
        }

        //requests a list of matches from the server in the form of String ID's
        //Returns a list of those stringID's
        public List<GameSession> Refresh()
        {
            //server get games
            //send the server REFRESH
            reader = new BinaryReader(socketStream);
            writer = new BinaryWriter(socketStream);
            //writer.Flush();
            //socketStream.Flush();

            writer.Write(Convert.ToByte( NetMessages.REFRESH));

            


            //build a list of games

            //format messages as ##@@@@@@@@@@@@@B
            //where ## is the INT ID of the game
            //where @@@@@@@@@@@@@ is a string up to 13 characters in length
            //if the ID is !0 then there are no more matches to be advertised
            //and B is a Integer boolean indicating if the game is full;

            List<GameSession> activeSessions = new List<GameSession>();
            char[] message = new char[16];
            string msg = "";
            string matchID = "";
            string matchName;
            bool isFull;
            GameSession temp;
            
            do
            {
                
                try
                {
                    if (!Helpers.IsNumber((char)reader.PeekChar()))
                    {
                        reader.ReadChar();

                    }
                    message = reader.ReadChars(16);
                    msg = "";
                    foreach (char c in message)
                    {
                        if(c != '*')
                            msg += c;
                    }
                    //msg = msg.Substring(1);

                    msg.TrimEnd();
                    matchID = msg.Substring(0, 2);
                    if (msg.Length > 2)
                    {
                        matchName = msg.Substring(2, msg.Length - 3);

                        if (msg.Substring(msg.Length - 1) == "1")
                            isFull = true;
                        else
                            isFull = false;

                        activeSessions.Add(new GameSession(Convert.ToUInt16(matchID), matchName, isFull));
                    }
                    
                }
                catch (IOException ioe)
                {
                    //handle it.
                }
                catch (FormatException fe)
                {
                    //can't convert matchID to number;
                }
                catch (Exception any)
                {
                    //Unexpected error
                }


            } while (matchID != "!0");

            
            return activeSessions;
        }


        public int Join(int gameID)
        {
            try
            {
                //tell server player wants to join gameID
                writer.Write(Convert.ToByte(NetMessages.JOIN));


                //send the match ID
                string sendString;
                if (gameID < 10)
                    sendString = "0" + gameID;
                else
                    sendString = gameID.ToString();

                writer.Write(sendString);


                //writer.Write(Convert.ToByte(NetMessages.TERMINATE));

                return 0;
            }
            catch (Exception any)
            {
                return -1;
            }
            
        }

        public void Disconnect()
        {
            writer.Write(Convert.ToByte(NetMessages.TERMINATE));
            writer.Close();
            reader.Close();
            //connection.Close(); - Causes error 
            socketStream.Close();
            client.Close();
        }

        

        public  void CreateMatch(string matchName, int hostPortID)
        {

            writer.Write(Convert.ToByte(NetMessages.CREATE));
            //this limits the server to 99 lifetime connections (since I have not implemented any form or recycling and the message size limitation).
            string transmissionString;

            if (hostPortID < 10)
            {
                transmissionString = "0" + hostPortID + matchName;
            }
            else
            {
                transmissionString = hostPortID + matchName;
            }

            if (transmissionString.Length < 15)
            {
                //fluff it up to match the expected size
                for (int i = transmissionString.Length; i < 15; i++)
                {
                    transmissionString += " ";
                }
            }
            writer.Flush();
            writer.Write(transmissionString);
            Thread waitForGameReady = new Thread(new ThreadStart(WaitForGameReady));
            waitForGameReady.Start();
            State = GameState.WAITING;
            connState = ServerConnectionState.CONNECTED;
            
            
        }

        public void WaitForGameReady()
        {
            NetMessages nMsg;
            //int c = reader.PeekChar();
            //while(reader.PeekChar() == -1)
            //{
                
            //}
            nMsg = NetMessages.NULL;
            do
            {
                try
                {
                    if(reader.PeekChar() != 0)
                        nMsg = (NetMessages)reader.ReadByte();
                }
                catch (Exception any)
                {
                    //do nothing
                }
            } while (nMsg != NetMessages.JOIN);
            nMsg = NetMessages.JOIN;
            writer.Write(Convert.ToByte(NetMessages.TERMINATE));
            State = GameState.PLAYING;
            

        }
    }
}
